/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "Spline.h"
#include "Vector3i.h"

class SplineToTree
{
	static inline bool isNeighbor(const Vector3i &p1, const Vector3i &p2)
	{
		return (p1.x != p2.x || p1.y != p2.y || p1.z != p2.z)
			&& (std::abs(p1.x-p2.x) < 2 && std::abs(p1.y-p2.y) < 2 && std::abs(p1.z-p2.z) < 2);
	}
public:
	static void rasterSpline(std::vector<Vector3i> &out, const Spline &spline)
	{
		double t = 0;
		double offset = 0.01;
		double splineLength = spline.getLength();
		double lastT = splineLength-0.000001;
		out.push_back(Vector3i(spline.sample(t)));
		while (t < splineLength)
		{
			double tOld = t;
			Vector3i lastSample(out[out.size()-1]);
			t += offset;
			Vector3i nextSample(spline.sample(t < splineLength ? t : lastT));
			while (!isNeighbor(lastSample, nextSample) || t <= tOld)
			{
				vAssert(offset != 0.0)
				if (lastSample == nextSample || t < tOld)
				{
					if (t >= splineLength) break;
					if (offset > 0) offset *= 2.0;
					else offset *= -0.25;
				}
				else
				{
					if (offset < 0) offset *= 2.0;
					else offset *= -0.25;
				}
				t += offset;
				nextSample = spline.sample(t < splineLength ? t : lastT);
			}
			out.push_back(nextSample);		
		}
	};

	template<class UserData, class Ext>
	static void extrudeSplineIntoTree(NTree<Vector3i, UserData>& tree, const Spline& spline, const Ext& extruder)
	{
		std::vector<Vector3i> points;
		rasterSpline(points, spline);
		int max = 0;
		for (unsigned int i = 0; i < points.size(); i++)
		{
			if (std::abs(points[i].x) > max) max = std::abs(points[i].x);
			if (std::abs(points[i].y) > max) max = std::abs(points[i].y);
			if (std::abs(points[i].z) > max) max = std::abs(points[i].z);
		}
		Exponent expo = AuxMath::log2(max);
		tree.raise(expo+1);
		float t = 0;
		float tOffset = 1.0f / (points.size()-1);
		for (unsigned int i = 0; i < points.size(); i++)
		{
			extruder.extrude(tree, points[i], t);
			t += tOffset;
		}
	}
};
